package com.atguigu.gmall.activity.listener;

import com.atguigu.gmall.activity.mapper.SeckillGoodsMapper;
import com.atguigu.gmall.activity.redis.listener.CacheHelper;
import com.atguigu.gmall.activity.utils.DateUtil;
import com.atguigu.gmall.common.constant.MqConst;
import com.atguigu.gmall.common.constant.RedisConst;
import com.atguigu.gmall.model.activity.OrderRecode;
import com.atguigu.gmall.model.activity.SeckillGoods;
import com.atguigu.gmall.model.activity.UserRecode;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.rabbitmq.client.Channel;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.Exchange;
import org.springframework.amqp.rabbit.annotation.Queue;
import org.springframework.amqp.rabbit.annotation.QueueBinding;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import java.io.IOException;
import java.util.Date;
import java.util.List;

/**
 * @author 李旭
 * @date 2021/12/7 14:20
 * @Description: MQ的监听器 接收消息
 */
@Component
public class ActivityListener {

    @Autowired
    private SeckillGoodsMapper seckillGoodsMapper;
    @Autowired
    private RedisTemplate redisTemplate;

    /**
     * 接收开始秒杀的消息
     *
     * @param message
     * @param channel
     */
    @RabbitListener(bindings = @QueueBinding(
            value = @Queue(value = MqConst.QUEUE_SECKILL_USER, durable = "true", autoDelete = "false"),
            exchange = @Exchange(value = MqConst.EXCHANGE_DIRECT_SECKILL_USER),
            key = MqConst.ROUTING_SECKILL_USER
    ))
    public void seckillMessage(UserRecode userRecode, Message message, Channel channel) {
        try {
//            1、校验状态位
            Object status = CacheHelper.get(userRecode.getSkuId().toString());
            if (!"1".equals(status)) {
                //手动应答
                channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
                return;//抛弃此监听器
            }
//            2、校验用户是否来过了   Key(userId + ":" + skuId)  : Value setnx  false true
            Boolean isCome = redisTemplate.opsForValue().setIfAbsent(
                    RedisConst.SECKILL_USER +
                            userRecode.getUserId() +
                            ":" + userRecode.getSkuId(), "");//seckill:user:3:4
            if (!isCome) {
                //此用户来过了
                //手动应答
                channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
                return;//抛弃此监听器
            }
            //此用户没来过  这是第一次来
//            3、校验库存  防超卖
            String skuId = (String) redisTemplate.opsForList().leftPop(RedisConst.SECKILL_STOCK_PREFIX
                    + userRecode.getSkuId());
            if (null == skuId) {
                //抢购失败 没有货了
                //待定 ： 可以选择删除用户来过的记录

                //更新状态位
                redisTemplate.convertAndSend("seckillpush",
                        userRecode.getSkuId() + ":0");
                //手动应答
                channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
                return;//抛弃此监听器
            }
            //4:有货 抢购成功了 保存抢购资格到Redis缓存中 （方便查询
            OrderRecode orderRecode = new OrderRecode();
            orderRecode.setUserId(userRecode.getUserId());
            SeckillGoods seckillGoods = (SeckillGoods) redisTemplate.opsForHash().get(RedisConst.SECKILL_GOODS,
                    userRecode.getSkuId().toString());
            orderRecode.setSeckillGoods(seckillGoods);
            redisTemplate.opsForValue().set(RedisConst.SECKILL_ORDERS +
                    userRecode.getUserId() +
                    ":" + userRecode.getSkuId(), orderRecode);
            //TODO ... 后续处理
            channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
        } catch (Exception e) {
            e.printStackTrace();
        }

    }


    //接收将DB中数据压入缓存

    @RabbitListener(bindings = @QueueBinding(
            value = @Queue(value = MqConst.QUEUE_TASK_1, durable = "true", autoDelete = "false"),
            exchange = @Exchange(value = MqConst.EXCHANGE_DIRECT_TASK),
            key = MqConst.ROUTING_TASK_1
    ))
    public void receiverMessage(Message message, Channel channel) {
        System.out.println("开始加载DB中数据到缓存中.......");
        try {
            //1、加载DB中数据  当天的秒杀数据
            QueryWrapper<SeckillGoods> seckillGoodsQueryWrapper = new QueryWrapper<>();

            /**
             * select *
             * from seckill_goods
             * where `status` = '1' and stock_count > 0 and
             *   DATE_FORMAT(start_time,'%Y-%m-%d') = '2021-12-07'
             */
            seckillGoodsQueryWrapper.eq("status", "1");//审核通过 1  审核不通过0
            seckillGoodsQueryWrapper.gt("stock_count", 0);//剩余库存大于0
            seckillGoodsQueryWrapper.eq("DATE_FORMAT(start_time,'%Y-%m-%d')",
                    DateUtil.formatDate(new Date()));
            List<SeckillGoods> seckillGoodsList = seckillGoodsMapper.selectList(seckillGoodsQueryWrapper);
            //2:将数据压入缓存中  hash类型  散列类型
            if (!CollectionUtils.isEmpty(seckillGoodsList)) {
                for (SeckillGoods seckillGoods : seckillGoodsList) {
                    //判断是否已经加入到缓存中了
                    if (!redisTemplate.opsForHash().hasKey(RedisConst.SECKILL_GOODS,
                            seckillGoods.getSkuId().toString())) {
                        //1、每一个秒杀商品都要保存到缓存中
                        redisTemplate.opsForHash().put(RedisConst.SECKILL_GOODS,
                                seckillGoods.getSkuId().toString(),
                                seckillGoods);
                        //2：防止超卖 将秒杀商品本身 放到Redis缓存的List类型中
                        String[] skuIds = buildSeckillGoodsIds(seckillGoods.getSkuId()
                                , seckillGoods.getStockCount());
                        redisTemplate.opsForList().leftPushAll(RedisConst.SECKILL_STOCK_PREFIX
                                + seckillGoods.getSkuId(), skuIds);
                        //3:状态位  skuId:1 skuId:0
                        redisTemplate.convertAndSend("seckillpush",
                                seckillGoods.getSkuId() + ":1");

                    }

                }
            }
            channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    //秒杀商品本身
    //秒杀商品的数量  10
    private String[] buildSeckillGoodsIds(Long skuId, Integer stockCount) {
        String[] ids = new String[stockCount];
        for (Integer i = 0; i < stockCount; i++) {
            ids[i] = skuId.toString();
        }
        return ids;

    }
}
